//============================================================================
// Name        : MI-PAR-KRS.cpp
// Author      : Jindrich Besta
// Version     :
// Copyright   : 
// Description : MI-PAR-KRS problem solver
//============================================================================

#include <iostream>
#include <fstream>
#include <stdio.h>
#include <sstream>
#include "solver/Solver.h"
#include "objects/Board.h"
#include "objects/BoardException.h"
#include "parser/InputDataParser.h"
#include "parser/ParseException.h"
#include <time.h>
#include <ctime>
#include "mpi/mpi.h"

#include "logger/Logger.h"

#define RESULT_FILENAME "result.txt"

using namespace std;

#define LENGTH 100000

void appendIsoDate(std::stringstream& ss)
{
	time_t secs = time(0);
	tm *t = localtime(&secs);
	ss << t->tm_year+1900 << "-" << t->tm_mon+1 << "-" << t->tm_mday;
	//<< " " << t->tm_hour << ":" << t->tm_min << ":" << t->tm_sec;
}

int main(int argc, char* argv[]) {
	Board* board;
	InputDataParser* parser;
	Solver* solver;

	int myRank;
	int numberOfProcessors;
	double starttime, endtime;

	MPI_Init(&argc, &argv);
	MPI_Barrier(MPI_COMM_WORLD);
	starttime = MPI_Wtime();
	MPI_Comm_rank(MPI_COMM_WORLD, &myRank);
	MPI_Comm_size(MPI_COMM_WORLD, &numberOfProcessors);

	/**
	 * path to file is provided via first argument to the program
	 * if it is not specified, default value is used (see InputDataParser.h
	 * for default value)
	 */
	const char* filename = (argc > 1) ? argv[1] : DEFAULT_DATA_FILENAME;

	stringstream loggerFilenameSS;
	appendIsoDate(loggerFilenameSS);
	loggerFilenameSS << "-" << numberOfProcessors << "_CPU-" << filename << "-" << myRank << ".log";

	//processor 0 starts computation
	if (myRank == 0)
	{
		/**
		 * parse input file
		 */

		try {
			parser = new InputDataParser(filename);
			board = parser->parse();
		} catch (const ParseException& e)
		{
			cout << e.what();
			delete parser;
			MPI_Finalize();
			return -1;
		} catch (const BoardException& e2)
		{
			cout << e2.what();
			delete parser;
			MPI_Finalize();
			return -1;
		}

		solver = new Solver(board);
		solver->setMyRank(myRank);
		solver->setNumberOfProcessors(numberOfProcessors);
		solver->initLogger(loggerFilenameSS.str().c_str());
		solver->startSolve();

		stringstream rss;
		appendIsoDate(rss);
		rss << "-" << numberOfProcessors << "_CPU-" << filename << ".result";

		ofstream resultFile;
		resultFile.open(rss.str().c_str());
		solver->writeSolution(resultFile);
		resultFile.close();

		MPI_Barrier(MPI_COMM_WORLD);
		cout << "[" << myRank << "] Time after barrier: " << time(NULL) << endl;
		endtime = MPI_Wtime();

		stringstream tss;
		appendIsoDate(tss);
		tss << "-" << numberOfProcessors << "_CPU-" << filename << ".time";
		ofstream timeFile;
		timeFile.open(tss.str().c_str());
		timeFile << "Execution took " << (endtime - starttime) << " seconds" << endl;
		timeFile.close();

		delete parser;
		delete board;
	}
	else //myRank != 0
	{
		//other processors (1 .. n-1)
		solver = new Solver();
		solver->setMyRank(myRank);
		solver->setNumberOfProcessors(numberOfProcessors);
		solver->initLogger(loggerFilenameSS.str().c_str());
		solver->receiveBufferSizeMessage();
		solver->receiveSolutionBounds();
		//first try - queen
		solver->doSolve(NULL, -1);
		//second try - bishop
		if (solver->getBestMoves() > solver->getLowerBound())
		{
//			cout << "[" << myRank << "] Now starts BISHOP" << endl;
			solver->doSolve(NULL, -1);
		}

		MPI_Barrier(MPI_COMM_WORLD);
		cout << "[" << myRank << "] Time after barrier: " << time(NULL) << endl;
	}

	delete solver;

	MPI_Finalize();
	return 0;
}
